package com.yfqy.app.modules.integral.serviceimpl;

import com.yfqy.app.configure.Idgenerator.SnowflakeIdGenerator;
import com.yfqy.app.constant.GeneralConstants;
import com.yfqy.app.constant.SystemConfigConstants;
import com.yfqy.app.domain.entity.IntegralRecord;
import com.yfqy.app.domain.mapper.CustomerMapper;
import com.yfqy.app.domain.mapper.IntegralRecordMapper;
import com.yfqy.app.domain.mapper.SystemConfigMapper;
import com.yfqy.app.enums.ErrorCode;
import com.yfqy.app.enums.IntegralType;
import com.yfqy.app.exception.BusinessException;
import com.yfqy.app.modules.integral.service.SignService;
import com.yfqy.app.modules.integral.vo.SignInResultVO;
import com.yfqy.app.modules.system.dto.SignInRewardDTO;
import com.yfqy.app.modules.system.service.SystemConfigService;
import com.yfqy.app.util.AuthUserContext;
import com.yfqy.app.util.JSON;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
import java.util.List;

@Service
@Slf4j
public class SignServiceImpl implements SignService {

    // 签到类型常量
    // 积分记录类型常量
    private static final byte SIGN_IN_TYPE = 1;
    private static final byte ORDER_REWARD_TYPE = 2;
    private static final byte REGISTER_REWARD_TYPE = 3;
    private static final byte BIND_MOBILE_REWARD_TYPE = 4;
    private static final byte DEDUCTION_TYPE = 5;
    private static final String CONFIG_GROUP = "integral";

    @Resource
    private SystemConfigMapper systemConfigMapper;
    @Resource
    private CustomerMapper customerMapper;
    @Resource
    private IntegralRecordMapper integralRecordMapper;
    @Resource
    private SnowflakeIdGenerator idGenerator;
    @Resource
    private SystemConfigService systemConfigService;

    /**
     * 处理用户每日签到积分奖励
     *
     * @return 本次获得的积分数
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public SignInResultVO processDailySignIn() {
        Long userId = getCurrentUserId();
        LocalDate today = LocalDate.now();

        // 检查今天是否已经签到
        if (hasSignedToday(userId, today)) {
            throw new RuntimeException("今日已签到，请勿重复签到");
        }

        // 获取签到配置
        SignInRewardDTO config = getSignInRewardConfig();

        // 计算连续签到天数
        int continuousDays = calculateContinuousDays(userId);

        // 计算本次签到应得积分
        int rewardPoints = calculateRewardPoints(continuousDays + 1, config);

        // 未签到用户添加积分并获取当前用户积分余额
        int currentPoints = addAndGetPoints(userId, rewardPoints);

        // 记录积分变动
        recordPointsChange(userId, rewardPoints, currentPoints, continuousDays + 1);

        return new SignInResultVO(continuousDays + 1, rewardPoints);
    }

    private boolean hasSignedToday(long userId, LocalDate today) {
        Integer count = integralRecordMapper.hasSignedToday(userId, SIGN_IN_TYPE, today);
        return count != null && count > 0;
    }

    private SignInRewardDTO getSignInRewardConfig() {
        String configJson = systemConfigMapper.querySignInRewardConfig();
        try {
            return JSON.parse(configJson, SignInRewardDTO.class);
        } catch (Exception e) {
            throw new RuntimeException("签到奖励配置解析失败", e);
        }
    }

    private int calculateContinuousDays(long userId) {
        try {
            IntegralRecord record = integralRecordMapper.getLatestRecord(userId, SIGN_IN_TYPE);
            if (record != null) {
                if (isContinuous(record.getCreatedTime())) {
                    // 从描述中解析连续签到天数
                    String description = record.getDescription();
                    if (description != null && description.startsWith("连续签到")) {
                        String daysStr = description.substring(4, description.indexOf("天"));
                        return Integer.parseInt(daysStr);
                    }
                }
            }
        } catch (Exception e) {
            // 无记录或其他异常
        }
        return 0; // 昨天没有签到，连续天数重置为0
    }

    public boolean isContinuous(Date createdTime) {
        // 将 Date 转换为 LocalDateTime
        LocalDateTime createdDateTime = createdTime.toInstant()
                .atZone(ZoneId.systemDefault())
                .toLocalDateTime();

        // 加一天
        LocalDateTime nextDay = createdDateTime.plusDays(1);

        // 转换为 LocalDate 进行比较（只比较日期部分）
        LocalDate nextDayDate = nextDay.toLocalDate();
        LocalDate today = LocalDate.now();

        return nextDayDate.equals(today);
    }

    private int calculateRewardPoints(int continuousDays, SignInRewardDTO config) {
        // 查找配置中对应的奖励
        for (int i = 0; i < config.getDays().size(); i++) {
            if (continuousDays <= config.getDays().get(i)) {
                return config.getPoints().get(i);
            }
        }

        // 如果连续天数超过配置的最大天数，返回最后一天的奖励
        return config.getPoints().get(config.getPoints().size() - 1);
    }

    private int addAndGetPoints(long userId, int rewardPoints) {
        try {
            customerMapper.addUserPoints(userId, rewardPoints);
            return customerMapper.getCustomerCurrentPoints(userId);
        } catch (Exception e) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR.getCode(), "获取用户积分失败");
        }
    }

    private void recordPointsChange(long userId, int points, int newBalance, int continuousDays) {

        IntegralRecord record = new IntegralRecord();
        record.setId(idGenerator.nextId());
        record.setUserId(userId);
        record.setPoints(points);
        record.setCurrentBalance(newBalance);
        record.setType(SIGN_IN_TYPE);
        record.setBusinessType(IntegralType.BusinessType.sign_in.name());
        record.setDescription(String.format("连续签到%d天", continuousDays));

        integralRecordMapper.insert(record);
    }

    /**
     * 获取用户本月签到情况
     *
     * @return 返回签到日期列表
     */
    @Override
    public List<String> getMonthSignInRecords() {
        Long userId = AuthUserContext.getUserId();
        LocalDate now = LocalDate.now();
        LocalDate firstDayOfMonth = now.withDayOfMonth(1);
        return integralRecordMapper.getMonthSignInRecords(userId, SIGN_IN_TYPE, now, firstDayOfMonth);
    }

    /**
     * 获取用户连续签到天数
     *
     * @return 连续签到天数
     */
    @Override
    public int getContinuousDays() {
        // 查询最近一次签到记录
        try {
            Long userId = AuthUserContext.getUserId();
            IntegralRecord record = integralRecordMapper.getLatestRecord(userId, SIGN_IN_TYPE);
            if (record != null) {
                String description = record.getDescription();
                if (description != null && description.startsWith("连续签到")) {
                    String daysStr = description.substring(4, description.indexOf("天"));
                    int days = Integer.parseInt(daysStr);

                    // 检查是否是昨天签到的
                    LocalDate today = LocalDate.now();
                    LocalDate yesterday = today.minusDays(1);

                    boolean hasSignedYesterday = hasSignedToday(userId, yesterday);

                    // 如果昨天有签到，且今天还没签到，则返回昨天的连续天数
                    if (hasSignedYesterday && !hasSignedToday(userId, today)) {
                        return days;
                    }

                    // 如果今天已经签到，返回今天的连续天数
                    if (hasSignedToday(userId, today)) {
                        return days;
                    }
                }
            }
        } catch (Exception e) {
            // 无记录或其他异常
            log.warn("无记录或其他异常", e);
        }

        return 0;
    }

    @Override
    public SignInRewardDTO signConfig() {
        String configValue = systemConfigService.getConfigValue(
                SystemConfigConstants.CONFIG_GROUP_INTEGRAL, SystemConfigConstants.CONFIG_KEY_SIGN_REWARD);
        return JSON.parse(configValue, SignInRewardDTO.class);
    }

    private Long getCurrentUserId() {
        Long userId = AuthUserContext.getUserId();
        if (GeneralConstants.DEFAULT_USER_ID.equals(userId)) {
            throw new BusinessException(ErrorCode.USER_NOT_LOGIN);
        }
        return userId;
    }

}
